
#include <iostream>
#include <sstream>
#include "Parser.h"
#include "Type.h"
#include "Code.h"

namespace mjc {

	Parser::Parser(std::wstreambuf *inbuf)
		:currentMethod(NULL),
		scanner(inbuf),
		symbolTable(),
		code(new Code),
		errorCount(0),
		errorDistance(3)
	{
		InitBitsets();
	}

	Parser::Parser(Scanner& scanner)
		:currentMethod(NULL),
		scanner(scanner),
		symbolTable(),
		code(new Code),
		errorCount(0),
		errorDistance(3)
	{
		InitBitsets();
	}

	Parser::~Parser()
	{
		delete code;
	}

	void Parser::InitBitsets()
	{
		exprStart.set(TokenType::T_IDENT);
		exprStart.set(TokenType::T_NUMBER);
		exprStart.set(TokenType::T_CHAR);
		exprStart.set(TokenType::T_NEW);
		exprStart.set(TokenType::T_LPAR);
		exprStart.set(TokenType::T_MINUS);

		statStart.set(TokenType::T_IDENT);
		statStart.set(TokenType::T_IF);
		statStart.set(TokenType::T_WHILE);
		statStart.set(TokenType::T_READ);
		statStart.set(TokenType::T_RETURN);
		statStart.set(TokenType::T_PRINT);
		statStart.set(TokenType::T_LBRACE);
		statStart.set(TokenType::T_SEMICOLON);

		statSeqFollow.set(TokenType::T_RBRACE);
		statSeqFollow.set(TokenType::T_EOF);

		declStart.set(TokenType::T_FINAL);
		declStart.set(TokenType::T_IDENT);
		declStart.set(TokenType::T_CLASS);

		declFollow.set(TokenType::T_LBRACE);
		declFollow.set(TokenType::T_VOID);
		declFollow.set(TokenType::T_EOF);
	}

	void Parser::AddObject(Object& object)
	{
		if(!symbolTable.GetScope()->AddObject(object))
		{
			std::wstringstream ss;
			ss << L"'" << object.GetName() << L"' declared more than once";
			Error(ss.str());
		}
	}

	void Parser::Scan()
	{
		currentToken = lookaheadToken;
		lookaheadToken = scanner.NextToken();
		++errorDistance;
	}

	void Parser::Check(const TokenType::Type& expected)
	{
		if(lookaheadToken.type == expected)
			Scan();
		else
		{
			std::wstring tokenName;
			if(TokenData::GetTokenName(expected, tokenName))
				Error(tokenName + L" expected");
			else
				Error(L"Unknown token " + tokenName);
		}
	}

	void Parser::Error(const std::wstring &msg)
	{
		if(errorDistance >= 3)
		{
			std::wcout << L"[error] line " << lookaheadToken.line << L", column " <<
						lookaheadToken.column << L": " << msg << std::endl;
			++errorCount;
		}

		errorDistance = 0;
	}

	void Parser::Program()
	{
		Check(TokenType::T_PROGRAM);
		Check(TokenType::T_IDENT);

		symbolTable.GetScope()->OpenScope();

		//process all tokens in the beginning, var and class decls
		while(declStart[lookaheadToken.type])
		{
			switch(lookaheadToken.type)
			{
			case TokenType::T_FINAL:
				ConstDecl();
				break;

			case TokenType::T_IDENT:
				VarDecl();
				break;

			case TokenType::T_CLASS:
				ClassDecl();
				break;

			default:
				Error(L"Constant or variable or class declaration expected");
			}
		}

		Check(TokenType::T_LBRACE);
		//now on, methods only
		while(lookaheadToken.type == TokenType::T_IDENT ||
			  lookaheadToken.type == TokenType::T_VOID )
		{
			MethodDecl();
		}

		Check(TokenType::T_RBRACE);
		code->SetDataSize(symbolTable.GetScope()->GetLocals().size());

		symbolTable.GetScope()->CloseScope();
	}

	void Parser::ConstDecl()
	{
		Check(TokenType::T_FINAL);
		mjc::Type type(Type());

		Type::Kind k0(type.GetKind());
		if(k0 != Type::K_INT && k0 != Type::K_CHAR)
			Error(L"Constants' (final) type may only be 'int' or 'char'");

		Check(TokenType::T_IDENT);
		std::wstring name(currentToken.strValue);
		Check(TokenType::T_ASSIGN);

		switch(lookaheadToken.type)
		{
		case TokenType::T_CHAR:
			Check(TokenType::T_CHAR);
			if(k0 != Type::K_CHAR)
				Error(L"Incompatible types");
			break;

		case TokenType::T_NUMBER:
			Check(TokenType::T_NUMBER);
			if(k0 != Type::K_INT)
				Error(L"Incompatible types");
			break;

		default:
			{
				std::wstring charName, numberName;
				TokenData::GetTokenName(TokenType::T_CHAR, charName);
				TokenData::GetTokenName(TokenType::T_NUMBER, numberName);
				Error(charName + L" or " + numberName + L" expected");
			}
		}

		Object obj(Object::K_CONSTANT, name, type);
		obj.GetConstantData().SetValue(currentToken.value);
		
		AddObject(obj);

		Check(TokenType::T_SEMICOLON);
	}

	void Parser::VarDecl()
	{
		mjc::Type type(Type());

		Check(TokenType::T_IDENT);
		Object obj(Object::K_VARIABLE, currentToken.strValue, type);
		AddObject(obj);

		if(currentMethod)
			currentMethod->GetMethodData().AddLocal(obj);

		while(lookaheadToken.type == TokenType::T_COMMA)
		{
			Scan();
			Check(TokenType::T_IDENT);
			Object obj(Object::K_VARIABLE, currentToken.strValue, type);
			AddObject(obj);

			if(currentMethod)
				currentMethod->GetMethodData().AddLocal(obj);
		}

		Check(TokenType::T_SEMICOLON);
	}

	void Parser::ClassDecl()
	{
		Check(TokenType::T_CLASS);
		Check(TokenType::T_IDENT);
		Object obj(Object::K_TYPE, currentToken.strValue, mjc::Type(Type::K_CLASS));

		symbolTable.GetScope()->OpenScope();
		Check(TokenType::T_LBRACE);

		while(lookaheadToken.type == TokenType::T_IDENT)
			VarDecl();

		Check(TokenType::T_RBRACE);

		Type::ClassData& cd = obj.GetType().GetClassData();
		std::vector<Object>& locals = symbolTable.GetScope()->GetLocals();

		for(std::vector<Object>::iterator it = locals.begin(); it != locals.end(); ++it)
			cd.AddField(*it);

		symbolTable.GetScope()->CloseScope();
		AddObject(obj);
	}

	void Parser::MethodDecl()
	{
		mjc::Type type(Type::K_NONE);//if return type is void, K_NONE

		switch(lookaheadToken.type)
		{
		case TokenType::T_IDENT:
			type = Type();
			break;

		case TokenType::T_VOID:
			Scan();
			break;

		default:
			{
				std::wstring identName, voidName;
				TokenData::GetTokenName(TokenType::T_IDENT, identName);
				TokenData::GetTokenName(TokenType::T_VOID, voidName);
				Error(identName + L" or " + voidName + L" expected");
			}
		}

		Check(TokenType::T_IDENT);
		Object obj(Object::K_METHOD, currentToken.strValue, type);
		AddObject(obj);

		currentMethod = symbolTable.GetScope()->Find(currentToken.strValue);
		symbolTable.GetScope()->OpenScope();
		Check(TokenType::T_LPAR);

		if(lookaheadToken.type != TokenType::T_RPAR)
			FormPars();//adds parameters to currentMethod and set its number of parameters

		if(currentMethod->GetName() == L"main")
		{
			code->SetMainPc(code->GetPc());
			if(currentMethod->GetType().GetKind() != Type::K_NONE)
				Error(L"The 'main' method type must be 'void'");

			if(currentMethod->GetMethodData().GetNParameters() != 0)
				Error(L"The 'main' method must not have any parameters");
		}

		Check(TokenType::T_RPAR);

		while(lookaheadToken.type == TokenType::T_IDENT)
			VarDecl();//this add locals to the currentMethod

		//generate codes
		currentMethod->GetMethodData().SetAddress(code->GetPc());
		code->Put(Code::C_ENTER);
		code->Put(currentMethod->GetMethodData().GetNParameters());
		code->Put(currentMethod->GetMethodData().GetLocalsCount()-currentMethod->GetMethodData().GetNParameters());

		Block();

		if(currentMethod->GetType().GetKind() == Type::K_NONE)
		{
			code->Put(Code::C_EXIT);
			code->Put(Code::C_RETURN);
		}
		else
		{
			code->Put(Code::C_TRAP);
			code->Put(1);
		}

		symbolTable.GetScope()->CloseScope();
		currentMethod = NULL;
	}

	Type Parser::Type()
	{
		Check(TokenType::T_IDENT);
		Object* obj = symbolTable.GetScope()->Find(currentToken.strValue);
		mjc::Type type(Type::K_NONE);

		if(obj == NULL)
		{
			Error(L"'" + currentToken.strValue + L"':undeclared identifier");
		}
		else if(obj->GetKind() != Object::K_TYPE)
		{
			Error(L"Type expected. Got '" + currentToken.strValue + L"'");
		}
		else
			type = obj->GetType();

		if(lookaheadToken.type == TokenType::T_LBRACK)//this is an array
		{
			Scan();
			Check(TokenType::T_RBRACK);
			mjc::Type t(Type::K_ARRAY);
			t.GetArrayData().SetType(type);
			return t;
		}

		return type;
	}

	void Parser::FormPars()
	{
		mjc::Type type(Type());
		Check(TokenType::T_IDENT);
		Object obj(Object::K_VARIABLE, currentToken.strValue, type);
		AddObject(obj);

		if(currentMethod)
			currentMethod->GetMethodData().AddLocal(obj);

		int nPar = 1;

		while(lookaheadToken.type == TokenType::T_COMMA)
		{
			Scan();
			mjc::Type type(Type());
			Check(TokenType::T_IDENT);
			Object obj(Object::K_VARIABLE, currentToken.strValue, type);
			AddObject(obj);

			if(currentMethod)
				currentMethod->GetMethodData().AddLocal(obj);

			++nPar;
		}
		
		if(currentMethod)
			currentMethod->GetMethodData().SetNParameters(nPar);
	}

	void Parser::Block()
	{
		Check(TokenType::T_LBRACE);

		while(statStart[lookaheadToken.type])
			Statement();

		Check(TokenType::T_RBRACE);
	}

	void Parser::Statement()
	{
		switch(lookaheadToken.type)
		{
		case TokenType::T_IDENT:
			{
				Item item(Designator());

				switch(lookaheadToken.type)
				{
				case TokenType::T_ASSIGN:
					{
						if(item.GetObject().GetKind() != Object::K_VARIABLE)
							Error(L"Variable or array element or object field expected");

						Scan();
						Item expr(Expr());
						
						if(expr.GetType().Assignable(item.GetType()))
							code->Assign(item, expr);
						else
							Error(L"Non assignable types");
					}
					break;

				case TokenType::T_LPAR:
					ActPars(item);
					code->Put(Code::C_CALL);
					code->Put2(item.GetAddress());

					if(item.GetType().GetKind() != Type::K_NONE)
						code->Put(Code::C_CALL);
					break;
				}

				Check(TokenType::T_SEMICOLON);
			}
			break;

		case TokenType::T_IF:
			{
				Scan();
				Check(TokenType::T_LPAR);
				Op op = Condition();
				Check(TokenType::T_RPAR);

				code->FalseJump(op, 0);
				int address0 = code->GetPc()-2;

				Statement();

				if(lookaheadToken.type == TokenType::T_ELSE)
				{
					code->Jump(0);
					int address1 = code->GetPc()-2;
					code->Fixup(address0);
					Scan();
					Statement();
					code->Fixup(address1);
				}
				else
					code->Fixup(address0);
			}
			break;

		case TokenType::T_WHILE:
			{
				int top = code->GetPc();
				Scan();
				Check(TokenType::T_LPAR);
				Op op = Condition();
				Check(TokenType::T_RPAR);

				code->FalseJump(op, 0);
				int address = code->GetPc()-2;

				Statement();

				code->Jump(top);
				code->Fixup(address);
			}
			break;

		case TokenType::T_RETURN:
			Scan();

			if(currentMethod == NULL)
				Error(L"'return' outside a method");

			if(exprStart[lookaheadToken.type])
			{
				Item item(Expr());
				code->Load(item);

				if(currentMethod)
				{
					if(currentMethod->GetType() == symbolTable.GetNoType())
						Error(L"Methods of type 'void' must not return values");
					else if(!currentMethod->GetType().Assignable(item.GetType()))
						Error(L"Return type is not assignable to its method type");
				}
			}
			else
			{
				if(currentMethod && currentMethod->GetType() != symbolTable.GetNoType())
					Error(L"Return value expected");
			}
			
			code->Put(Code::C_EXIT);
			code->Put(Code::C_RETURN);
			Check(TokenType::T_SEMICOLON);
			break;

		case TokenType::T_READ:
			{
				Scan();
				Check(TokenType::T_LPAR);

				Item item(Designator());

				if(item.GetObject().GetKind() != Object::K_VARIABLE)
					Error(L"Variable or array element or object field expected");

				if(item.GetType().GetKind() == Type::K_INT)
					code->Put(Code::C_READ);
				else if(item.GetType().GetKind() == Type::K_CHAR)
					code->Put(Code::C_BREAD);
				else
					Error(L"'char' or 'int' expression expected");
				
				code->Assign(item);
				Check(TokenType::T_RPAR);
				Check(TokenType::T_SEMICOLON);
			}
			break;

		case TokenType::T_PRINT:
			{
				Scan();
				Check(TokenType::T_LPAR);
				Item item(Expr());
				code->Load(item);

				if(lookaheadToken.type == TokenType::T_COMMA)
				{
					Scan();
					Check(TokenType::T_NUMBER);
					Object obj(Object::K_CONSTANT, currentToken.strValue, mjc::Type(Type::K_INT));
					obj.GetConstantData().SetValue(currentToken.value);
					code->Load(Item(obj));
				}
				else
					code->Put(Code::C_CONST1);
				
				if(item.GetType().GetKind() == Type::K_INT)
					code->Put(Code::C_PRINT);
				else if(item.GetType().GetKind() == Type::K_CHAR)
					code->Put(Code::C_BPRINT);
				else
					Error(L"'char' or 'int' expression expected");

				Check(TokenType::T_RPAR);
				Check(TokenType::T_SEMICOLON);
			}
			break;

		case TokenType::T_LBRACE:
			Block();
			break;

		default: 
			Check(TokenType::T_SEMICOLON);

		}
	}

	Item Parser::Designator()
	{
		Check(TokenType::T_IDENT);
		Object* pObj = symbolTable.GetScope()->Find(currentToken.strValue);
		Item item(Object(symbolTable.GetNoObject()));

		if(pObj == NULL)
			Error(L"'" + currentToken.strValue + L"':undeclared identifier");
		else
			item = Item(*pObj);

		while(true)
		{
			Object obj = item.GetObject();

			if(lookaheadToken.type == TokenType::T_PERIOD)
			{
				if(obj.GetType().GetKind() != Type::K_CLASS)
					Error(L"Class expected at left of '.'");

				Scan();
				Check(TokenType::T_IDENT);

				if(obj.GetType().GetKind() == Type::K_CLASS)
				{
					code->Load(item);
					Object* field = obj.GetType().GetClassData().FindField(currentToken.strValue);

					if(field == NULL)
						Error(L"Field '" + currentToken.strValue + L"' not found in class '" + obj.GetName() + L"'");
					else
					{
						Object o(*field);
						item = Item(o, Item::K_FIELD);
					}
				}
			}
			else if(lookaheadToken.type == TokenType::T_LBRACK)
			{
				if(obj.GetType().GetKind() != Type::K_ARRAY)
					Error(L"Array expected at left of '['");
				Scan();
				code->Load(item);
				Item expr(Expr());

				if(obj.GetType().GetKind() == Type::K_ARRAY)
				{
					if(expr.GetType().GetKind() == Type::K_INT)
					{	
						code->Load(expr);
						Object o = Object(Object::K_VARIABLE, obj.GetName()+L"[]", obj.GetType().GetArrayData().GetType());
						item = Item(o, Item::K_ELEMENT);
					}
					else
						Error(L"Expression inside brackets must be of type 'int'");
				}

				Check(TokenType::T_RBRACK);
			}
			else 
				break;
		}

		return item;
	}
	
	void Parser::ActPars(Item& item)
	{
		bool isMethod = item.GetKind() == Item::K_METHOD;
		
		if(!isMethod)
			Error(L"Method expected");

		Check(TokenType::T_LPAR);
		unsigned int paramCount = 0;

		if(exprStart[lookaheadToken.type])
		{
			Item param(Expr());
			code->Load(param);
			++paramCount;

			if(isMethod)
			{
				Object::MethodData& md = item.GetObject().GetMethodData();

				if(md.GetNParameters() > 0)
				{
					if(!param.GetType().Assignable(md.GetLocal(0).GetType()))
						Error(L"Incompatible types");
				}
				else
					Error(L"Method takes no parameters");
			}

			while(lookaheadToken.type == TokenType::T_COMMA)
			{
				Scan();
				param = Expr();
				code->Load(param);
				++paramCount;

				if(isMethod)
				{
					Object::MethodData& md = item.GetObject().GetMethodData();

					if(md.GetNParameters() <= paramCount)
					{
						if(!param.GetType().Assignable(md.GetLocal(paramCount-1).GetType()))
							Error(L"Incompatible types");
					}
					else
					{
						std::wstringstream ss;
						ss << L"Method takes only " << (paramCount-1) << L" parameters";
						Error(ss.str());
					}
				}
			}
		}

		if(isMethod)
		{
			Object::MethodData& md = item.GetObject().GetMethodData();

			if(paramCount < md.GetNParameters())
				Error(L"Not enough parameters for method");
		}

		Check(TokenType::T_RPAR);
	}

	Parser::Op Parser::Condition()
	{
		Item item0(Expr());
		code->Load(item0);
		Op op = Relop();
		Item item1(Expr());
		code->Load(item1);

		if(!item0.GetType().Compatible(item1.GetType()))
			Error(L"Incompatible types");
		else if(item0.GetType().IsReferenceType() && op != OP_EQ && op != OP_NE)
			Error(L"Classes and arrays can only be checked for equality or inequality");

		return op;
	}

	Item Parser::Expr()
	{
		bool minus = false;

		if(lookaheadToken.type == TokenType::T_MINUS)
		{
			Scan();
			minus = true;
		}

		Item item(Term());

		if(minus && item.GetType().GetKind() != Type::K_INT)
			Error(L"Term after '-' must be of type 'int'");

		if(minus)
		{
			if(item.GetKind() == Item::K_CONSTANT)
			{
				item.SetValue(-item.GetValue());
				code->Load(item);
			}
			else
			{
				code->Load(item);
				code->Put(Code::C_NEG);
			}
		}
		else
			code->Load(item);

		while(lookaheadToken.type == TokenType::T_PLUS ||
			  lookaheadToken.type == TokenType::T_MINUS )
		{
			minus = lookaheadToken.type == TokenType::T_MINUS;
			
			Scan();
			Item term(Term());
			code->Load(term);
			
			if(item.GetType().GetKind() != Type::K_INT || term.GetType().GetKind() != Type::K_INT)
				Error(L"Terms of type 'int' expected");
			
			if(minus)
				code->Put(Code::C_SUB);
			else
				code->Put(Code::C_ADD);
		}	

		return item;
	}

	Parser::Op Parser::Relop()
	{
		Op op = OP_UNKNOWN;

		switch(lookaheadToken.type)
		{
		case TokenType::T_EQL:
			op = OP_EQ;
			break;

		case TokenType::T_NEQ:
			op = OP_NE;
			break;

		case TokenType::T_GTR:
			op = OP_GT;
			break;

		case TokenType::T_GEQ:
			op = OP_GE;
			break;

		case TokenType::T_LSS:
			op = OP_LT;
			break;

		case TokenType::T_LEQ:
			op = OP_LE;
			break;

		default:
			{
				std::wstringstream ss;
				std::wstring name;

				ss << L"( ";
				TokenData::GetTokenName(TokenType::T_EQL, name);
				ss << name << L" | ";
				TokenData::GetTokenName(TokenType::T_NEQ, name);
				ss << name << L" | ";
				TokenData::GetTokenName(TokenType::T_GTR, name);
				ss << name << L" | ";
				TokenData::GetTokenName(TokenType::T_GEQ, name);
				ss << name << L" | ";
				TokenData::GetTokenName(TokenType::T_LSS, name);
				ss << name << L" | ";
				TokenData::GetTokenName(TokenType::T_LEQ, name);
				ss << name << L" )";

				Error(ss.str() + L" expected");
			}
		}

		Scan();
		return op;
	}
		
	Item Parser::Term()
	{
		Item item(Factor());
		code->Load(item);

		while(lookaheadToken.type == TokenType::T_TIMES ||
			  lookaheadToken.type == TokenType::T_SLASH ||
			  lookaheadToken.type == TokenType::T_REM )
		{
			Code::C c = Code::C_MUL;

			switch(lookaheadToken.type)
			{
			case TokenType::T_SLASH:
				c = Code::C_DIV;
				break;

			case TokenType::T_REM:
				c = Code::C_REM;
				break;
			}

			Scan();
			
			Item factor(Factor());
			code->Load(factor);
			code->Put(c);
			
			if(item.GetType().GetKind() != Type::K_INT || factor.GetType().GetKind() != Type::K_INT)
				Error(L"Terms of type 'int'expected");
		}

		return item;
	}
		
	Item Parser::Factor()
	{
		switch(lookaheadToken.type)
		{
		case TokenType::T_IDENT:
			{
				Item item(Designator());

				if(lookaheadToken.type == TokenType::T_LPAR)
				{
					Object& obj(item.GetObject());

					ActPars(item);
					
					if(item.GetObject().GetName() != symbolTable.GetChrObject().GetName() &&
					   item.GetObject().GetName() != symbolTable.GetOrdObject().GetName())
					{
						if(item.GetObject().GetName() == symbolTable.GetOrdObject().GetName())
							code->Put(Code::C_ARRAYLENGTH);
						else
						{
							code->Put(Code::C_CALL);
							code->Put2(item.GetAddress());
						}
					}
				}

				//item.SetKind(Item::K_STACK);
				return item;
			}
			break;

		case TokenType::T_NUMBER:
			{
				Scan();
				Object obj(Object::K_CONSTANT, currentToken.strValue, Type::K_INT);
				obj.GetConstantData().SetValue(currentToken.value);
				Item item(obj);
				code->Load(item);
				return item;
			}
			break;

		case TokenType::T_CHAR:
			{
				Scan();
				Object obj(Object::K_CONSTANT, currentToken.strValue, Type::K_CHAR);
				obj.GetConstantData().SetValue(currentToken.value);
				Item item(obj);
				code->Load(item);
				return item;
			}
			break;

		case TokenType::T_NEW:
			{
				Scan();
				Check(TokenType::T_IDENT);
				Object* pObj = symbolTable.GetScope()->Find(currentToken.strValue);
				Object obj(symbolTable.GetNoObject());

				if(pObj == NULL)
					Error(L"'" + currentToken.strValue + L"':undeclared identifier");
				else
				{
					obj = *pObj;

					if(obj.GetKind() != Object::K_TYPE)
						Error(L"Type exepected");
				}
				
				if(lookaheadToken.type == TokenType::T_LBRACK)
				{
					Scan();
					Item item(Expr());

					if(item.GetType().GetKind() != Type::K_INT)
						Error(L"Expression inside brackets must be of type 'int'");

					if(obj.GetType().GetKind() != Type::K_NONE)
					{
						code->Load(item);
						code->Put(Code::C_NEWARRAY);

						if(obj.GetType().GetKind() == Type::K_CHAR)
							code->Put(0);
						else
							code->Put(1);

						mjc::Type t(obj.GetType());
						obj = Object(Object::K_VARIABLE, obj.GetName(), mjc::Type(Type::K_ARRAY));
						obj.GetType().GetArrayData().SetType(t);
					}

					Check(TokenType::T_RBRACK);
				}
				else
				{
					obj = Object(Object::K_VARIABLE, obj.GetName(), obj.GetType());

					if(obj.GetType().GetKind() != Type::K_CLASS)
						Error(L"Type after 'new' must denote a class");
					else
					{
						code->Put(Code::C_NEW);
						code->Put2(obj.GetType().GetClassData().FieldCount());
					}
				}

				return Item(obj, Item::K_STACK);
			}
			break;

		case TokenType::T_LPAR:
			{
				Scan();
				Item item(Expr());
				Check(TokenType::T_RPAR);
				return item;
			}
			break;

		default:
			Error(L"Factor expected");
		}
		
		return Item(symbolTable.GetNoObject());
	}

	void Parser::Parse()
	{
		Scan();
		Program();

		if(lookaheadToken.type != TokenType::T_EOF)
			Error(L"End of file found before end of program");
	}

	void Parser::PrintSymbolTable()
	{
		std::wcout << *symbolTable.GetScope() << std::endl;
	}
}